package com.keba.lamp.complaint.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.keba.basic.base.R;
import com.keba.basic.context.ContextUtil;
import com.keba.basic.database.mybatis.conditions.Wraps;
import com.keba.basic.echo.properties.EchoProperties;
import com.keba.basic.utils.BeanPlusUtil;
import com.keba.basic.utils.DateUtils;
import com.keba.lamp.complaint.entity.AccountPrepayment;
import com.keba.lamp.complaint.entity.Case;
import com.keba.lamp.complaint.enumeration.CaseStateEnum;
import com.keba.lamp.complaint.service.AccountPrepaymentService;
import com.keba.lamp.complaint.service.CaseService;
import com.keba.lamp.complaint.vo.result.CaseDayResultVO;
import com.keba.lamp.complaint.vo.result.CaseMonthResultVO;
import com.keba.lamp.complaint.vo.result.CaseNumReportResultVO;
import com.keba.lamp.model.constant.EchoDictType;
import com.keba.lamp.system.manager.system.DefDictManager;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@RequiredArgsConstructor
@Validated
@RestController
@RequestMapping("/report")
@Tag(name = "报表统计")
public class ReportController {
    private final CaseService caseService;
    private final EchoProperties ips;

    private final DefDictManager defDictManager;

    private final AccountPrepaymentService accountPrepaymentService;

    // 报表统计
    @Operation(summary = "租户-案件数量统计报表")
    @GetMapping("/tenant/caseNumReport")
    public R<CaseNumReportResultVO> getTenantCaseNumReport() {
        return R.success(buildCaseNum(ContextUtil.getTenantId()));
    }

    /**
     * 按每日、每月统计
     *
     * @param tenantId
     * @return
     */
    @Operation(summary = "租户-每日、月案件数量统计报表", parameters = {
            @Parameter(name = "type", description = "统计类型：day-每日，month-每月", required = true, example = "day")
    })
    @GetMapping("/tenant/caseDayMonthReport")
    public R<Map<String, Long>> listTenantCaseDayMonthReport(@RequestParam(defaultValue = "day") String type) {
        return R.success(buildCaseDayMonth(ContextUtil.getTenantId(), type));
    }


    /**
     * 按案件类型分组，每月案件数量统计报表
     *
     * @param tenantId
     * @return
     */
    @Operation(summary = "租户-按案件类型分组，每月案件数量统计报表", parameters = {
            @Parameter(name = "dayOrMonth", description = "按月份统计", required = true, example = "2025-01")
    })
    @GetMapping("/tenant/caseTypeDayMonthReport")
    public R<Map<String, Map<String, Long>>> listTenantCaseTypeDayMonthReport(@RequestParam String dayOrMonth) {
        return R.success(buildCaseTypeDayMonth(ContextUtil.getTenantId(), dayOrMonth));
    }


    @Operation(summary = "平台-案件数量统计报表")
    @GetMapping("/caseNumReport")
    public R<CaseNumReportResultVO> getCaseNumReport(@RequestParam(required = false) Long tenantId) {
        tenantId = getCurrentTenantId(tenantId);
        return R.success(buildCaseNum(tenantId));
    }


    /**
     * 按每日、每月统计
     *
     * @param tenantId
     * @return
     */
    @Operation(summary = "平台-每日、月案件数量统计报表", parameters = {
            @Parameter(name = "tenantId", description = "租户ID", required = false, example = "1"),
            @Parameter(name = "type", description = "统计类型：day-每日，month-每月", required = true, example = "day")
    })
    @GetMapping("/caseDayMonthReport")
    public R<Map<String, Long>> listCaseDayMonthReport(@RequestParam(required = false) Long tenantId, @RequestParam(defaultValue = "day") String type) {
        tenantId = getCurrentTenantId(tenantId);
        return R.success(buildCaseDayMonth(tenantId, type));
    }


    /**
     * 按案件类型分组，每月案件数量统计报表
     *
     * @param tenantId
     * @return
     */
    @Operation(summary = "平台-按案件类型分组，每月案件数量统计报表", parameters = {
            @Parameter(name = "tenantId", description = "租户ID", required = false, example = "1"),
            @Parameter(name = "dayOrMonth", description = "按月份统计", required = true, example = "2025-01")
    })
    @GetMapping("/caseTypeDayMonthReport")
    public R<Map<String, Map<String, Long>>> listCaseTypeDayMonthReport(@RequestParam(required = false) Long tenantId, @RequestParam String dayOrMonth) {
        tenantId = getCurrentTenantId(tenantId);
        return R.success(buildCaseTypeDayMonth(tenantId, dayOrMonth));
    }


    private Map<String, Map<String, Long>> buildCaseTypeDayMonth(Long tenantId, String dayOrMonth) {
        Map<String, Map<String, Long>> resultMap = new LinkedHashMap<>(16);
        DateTime dateTime = DateUtil.parse(dayOrMonth, "yyyy-MM");
        Date beginOfMonth = DateUtil.beginOfMonth(dateTime);
        Date endOfMonth = DateUtil.endOfMonth(dateTime);
        Map<Serializable, Object> dictMap = defDictManager.findByIds(Collections.singleton(EchoDictType.System.CASE_TYPE));
        List<Case> cases = caseService.list(Wraps.<Case>lbQ().select(Case::getId, Case::getCaseType, Case::getCreatedTime).eq(Objects.nonNull(tenantId), Case::getTenantId, tenantId).eq(Case::getCaseState, CaseStateEnum.COMPLETE.getCode()).between(Case::getCreatedTime, beginOfMonth, endOfMonth));
        if (CollUtil.isEmpty(cases)) {
            dictMap.forEach((key, value) -> {
                resultMap.put((String) value, new HashMap<>(16));
            });
            return resultMap;
        }

        List<CaseDayResultVO> list = BeanPlusUtil.toBeanList(cases, CaseDayResultVO.class);
        Map<String, List<CaseDayResultVO>> caseTypeMap = list.stream().collect(Collectors.groupingBy(CaseDayResultVO::getCaseType));
        caseTypeMap.forEach((caseType, caseList) -> {
            resultMap.put(dictMap.get(StrUtil.join(ips.getDictSeparator(), EchoDictType.System.CASE_TYPE, caseType)).toString(), MapUtil.sort(caseList.stream().collect(Collectors.groupingBy(CaseDayResultVO::getDayMonth, Collectors.counting())), String::compareTo));
        });
        return resultMap;
    }

    private Map<String, Long> buildCaseDayMonth(Long tenantId, String type) {
        Map<String, Long> resultMap = new LinkedHashMap<>(16);
        if (StrUtil.equals(type, "day")) {
            Date beginOfMonth = DateUtil.beginOfMonth(DateUtil.date());
            Date endOfMonth = DateUtil.endOfMonth(DateUtil.date());
            List<String> days = DateUtils.getBetweenDay(DateUtils.date2LocalDate(beginOfMonth), DateUtils.date2LocalDate(endOfMonth), "MM月dd日");
            List<Case> cases = caseService.list(Wraps.<Case>lbQ().select(Case::getId, Case::getCreatedTime).eq(Objects.nonNull(tenantId), Case::getTenantId, tenantId).eq(Case::getCaseState, CaseStateEnum.COMPLETE.getCode()).between(Case::getCreatedTime, beginOfMonth, endOfMonth));
            if (CollUtil.isEmpty(cases)) {
                days.forEach(o -> {
                    resultMap.put(o, 0L);
                });
                return resultMap;
            }
            List<CaseDayResultVO> list = BeanPlusUtil.toBeanList(cases, CaseDayResultVO.class);
            Map<String, Long> map = list.stream().collect(Collectors.groupingBy(CaseDayResultVO::getDayMonth, Collectors.counting()));
            days.forEach(o -> {
                resultMap.put(o, map.getOrDefault(o, 0L));
            });
            return resultMap;
        }
        Date beginOfYear = DateUtil.beginOfYear(DateUtil.date());
        Date endOfYear = DateUtil.endOfYear(DateUtil.date());
        List<String> months = DateUtils.getBetweenMonth(DateUtils.date2LocalDate(beginOfYear), DateUtils.date2LocalDate(endOfYear), "MM月");
        List<Case> cases = caseService.list(Wraps.<Case>lbQ().select(Case::getId, Case::getCreatedTime).eq(Objects.nonNull(tenantId), Case::getTenantId, tenantId).eq(Case::getCaseState, CaseStateEnum.COMPLETE.getCode()).between(Case::getCreatedTime, beginOfYear, endOfYear));
        if (CollUtil.isEmpty(cases)) {
            months.forEach(o -> {
                resultMap.put(o, 0L);
            });
            return resultMap;
        }
        List<CaseMonthResultVO> list = BeanPlusUtil.toBeanList(cases, CaseMonthResultVO.class);
        Map<String, Long> map = list.stream().collect(Collectors.groupingBy(CaseMonthResultVO::getDayMonth, Collectors.counting()));
        months.forEach(o -> {
            resultMap.put(o, map.getOrDefault(o, 0L));
        });
        return resultMap;
    }


    private CaseNumReportResultVO buildCaseNum(Long tenantId) {
        CaseNumReportResultVO caseNumReportResultVO = CaseNumReportResultVO.builder().build();
        Long allCaseNum = caseService.getSuperManager().count(Wraps.<Case>lbQ().eq(Objects.nonNull(tenantId), Case::getTenantId, tenantId).eq(Case::getCaseState, CaseStateEnum.COMPLETE.getCode()));
        Long todayCaseNum = caseService.getSuperManager().count(Wraps.<Case>lbQ().eq(Objects.nonNull(tenantId), Case::getTenantId, tenantId)
                .ge(Case::getCreatedTime, DateUtil.beginOfDay(DateUtil.date())).le(Case::getCreatedTime, DateUtil.endOfDay(DateUtil.date()))
                .eq(Case::getCaseState, CaseStateEnum.COMPLETE.getCode()));
        Long yesterdayCaseNum = caseService.getSuperManager().count(Wraps.<Case>lbQ().eq(Objects.nonNull(tenantId), Case::getTenantId, tenantId)
                .ge(Case::getCreatedTime, DateUtil.beginOfDay(DateUtil.yesterday())).le(Case::getCreatedTime, DateUtil.endOfDay(DateUtil.yesterday()))
                .eq(Case::getCaseState, CaseStateEnum.COMPLETE.getCode()));
        Long monthCaseNum = caseService.getSuperManager().count(Wraps.<Case>lbQ().eq(Objects.nonNull(tenantId), Case::getTenantId, tenantId)
                .ge(Case::getCreatedTime, DateUtil.beginOfMonth(DateUtil.date())).le(Case::getCreatedTime, DateUtil.endOfMonth(DateUtil.date()))
                .eq(Case::getCaseState, CaseStateEnum.COMPLETE.getCode()));

        BigDecimal accountBalance = accountPrepaymentService.list(Wraps.<AccountPrepayment>lbQ().select(AccountPrepayment::getBalance).eq(Objects.nonNull(tenantId), AccountPrepayment::getTenantId, tenantId))
                .stream().map(AccountPrepayment::getBalance) // 提取 balance 字段
                .reduce(BigDecimal.ZERO, BigDecimal::add); // 累加
        caseNumReportResultVO.setAllCaseNum(allCaseNum).setTodayCaseNum(todayCaseNum).setYesterdayCaseNum(yesterdayCaseNum).setMonthCaseNum(monthCaseNum)
                .setAccountBalance(accountBalance);
        return caseNumReportResultVO;
    }

    private Long getCurrentTenantId(Long tenantId) {
        return tenantId == null && Convert.toLong(ContextUtil.getApplicationId(), 1L) != 2 ? ContextUtil.getTenantId() : tenantId;
    }


}
